Skip to content

feat(commands): add /link and /attach structured stash commands - #135

Merged
BunsDev merged 4 commits into
mainfrom
stash-commands
Jul 7, 2026
Merged

feat(commands): add /link and /attach structured stash commands#135
BunsDev merged 4 commits into
mainfrom
stash-commands

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds /link and /attach slash commands backed by a new structured stash store, so links and file attachments can be saved, tagged, searched, and managed durably across sessions.

Context

  • Related issue/user request: user request to "integrate slash commands to enable storing and saving links and attachments in a structured manner".
  • Scope: new claurst-core::stash module, /link + /attach commands, TUI autocomplete entries, docs.
  • Non-goals: syncing the stash anywhere (it is a personal local store), hosted-mode stash namespaces (the commands refuse in hosted review mode instead), injecting stash content into prompts.

Changes

  • core/stash.rs (new): SQLite store at ~/.coven-code/stash.sqlite (rusqlite, matching the goal-store pattern) with kind (link/attachment), value, original path, title, note, tags, project, session, and timestamp. Attachments are copied into ~/.coven-code/attachments/<full-uuid>/<filename> so the stored copy survives the original moving; removal deletes the row plus the stored copy and its directory. Lookups/removals accept an id prefix and are kind-scoped; ambiguous prefixes are rejected.
  • commands/lib.rs: LinkCommand (/link [add] <url>, list [--tag|--project], search, remove) and AttachCommand (/attach [add] <path>, list, search, show, remove), registered in the command registry. Quote-aware argument tokenizer (--title "API spec", quoted paths); unquoted attach paths with spaces also resolve by joining positionals. ~ and relative paths resolve against home/cwd. Both commands refuse to run in hosted review mode.
  • tui/app.rs: attach/link autocomplete entries (alphabetical, keeps prompt_slash_commands_covers_registry green).
  • docs/commands.md: /link and /attach reference sections plus ToC entry.

Code-review findings addressed before commit (reviewed via the code-review agent):

  • Kind-scoped get/remove so /link remove can never delete an attachment's stored file (High).
  • Full-uuid attachment directories (was 8-char prefix) + refuse existing destination, eliminating silent overwrite/cross-delete on prefix collisions (Medium).
  • Quote support in argument parsing so files/titles with spaces work (Medium).

Validation

  • git diff --check — clean
  • cargo fmt --all — clean
  • cargo check --workspace — clean, no warnings
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test --workspace — all green (10 new stash store tests, 8 new command tests, tui autocomplete coverage test)
  • Targeted/manual checks: live TUI smoke test via tmux — /attach "/tmp/stash smoke/my file.txt" --title "Quoted title" stores the copy under the full-uuid dir; /link remove <attachment-id> correctly refuses (no stash item matches); /attach remove deletes the row and stored copy. Test artifacts cleaned up.
  • Not run: release builds (not needed).

PR Readiness

  • Diff is limited to the intended files (5 files; unrelated in-flight work from other agents excluded).
  • Generated files were not edited by hand.
  • User-facing docs were updated (docs/commands.md).
  • No secrets, credentials, local paths, or unrelated logs are included.
  • Remaining risks: /attach add treats every positional after add as one path; a file literally named list/show/remove needs /attach add <name>. Stash content is never loaded into prompts — future work could add /link use <id>-style context injection.

Copilot AI review requested due to automatic review settings July 7, 2026 06:03
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview Jul 7, 2026 7:00am

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds durable, structured local storage for links and file attachments and exposes it via new /link and /attach slash commands, with TUI autocomplete and user-facing docs updates.

Changes:

  • Introduces claurst-core::stash SQLite-backed store for link/attachment metadata and attachment file copies under the user config dir.
  • Registers /link and /attach commands (with quote-aware flag parsing) and adds minimal tests for parsing + hosted-review refusal.
  • Updates TUI slash-command autocomplete list and documents the new commands in docs/commands.md.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src-rust/crates/tui/src/app.rs Adds attach/link to the slash-command autocomplete/help list.
src-rust/crates/core/src/stash.rs New SQLite stash store implementation + unit tests for core stash behaviors.
src-rust/crates/core/src/lib.rs Exposes the new stash module from claurst-core.
src-rust/crates/commands/src/lib.rs Implements /link + /attach, arg parsing helpers, command registration, and basic tests.
docs/commands.md Documents /link and /attach and updates the TOC entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src-rust/crates/core/src/stash.rs Outdated
session_id: session_id.map(str::to_string),
created_at_ms: Self::now_ms(),
};
self.insert(&item)?;
Comment on lines +337 to +348
let item = self.get(id_prefix, kind)?;
self.conn
.execute("DELETE FROM stash_items WHERE id = ?1", [&item.id])
.map_err(|e| StashError::Db(e.to_string()))?;
if item.kind == StashKind::Attachment {
let stored = Path::new(&item.value);
let _ = std::fs::remove_file(stored);
if let Some(dir) = stored.parent() {
let _ = std::fs::remove_dir(dir);
}
}
Ok(item)
Comment on lines +262 to +264
pub fn list(&self, filter: &StashFilter) -> Result<Vec<StashItem>, StashError> {
let mut items = self.query_all()?;
items.retain(|item| {
Comment thread docs/commands.md Outdated
/link remove <id> — delete a link
```

`/link list` shows each entry's short id, save date, title, URL, and tags. `--project` limits the listing to links saved from the current repository. The stash is a personal local store and is disabled in [hosted review mode](configuration#hosted-review-mode).
Comment on lines +10237 to +10241
async fn execute(&self, args: &str, ctx: &mut CommandContext) -> CommandResult {
if let Some(refusal) = stash_hosted_refusal(ctx) {
return refusal;
}
let (positional, flags) = parse_stash_args(args);
@BunsDev

BunsDev commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Addressed the review in ec913a6 and bea1f8b:

  • Orphaned stored copy on insert failure: add_attachment now removes the copied file and its directory when the row insert fails.
  • Removal ordering: remove() deletes the stored copy before the DB row; filesystem failures keep the row so removal can be retried, and an already-missing copy no longer blocks it.
  • Docs link fixed to ./configuration.md#hosted-review-mode.
  • Added an end-to-end /link+/attach round-trip test against a temp home (quoted path with spaces, kind-scoped removal refusal, stored-copy cleanup, list filtering).
  • The command palette TUI case is re-anchored on /attach//clear//compact since /attach now sorts first and pushed /config below the initial visible window.

Not changed: list/search still filter in Rust over query_all(). The stash is a personal local store (tens to hundreds of rows); pushing filters into SQL is a reasonable follow-up if it ever grows indexes worth using.

BunsDev and others added 3 commits July 7, 2026 01:44
Adds a durable, structured store for links and file attachments:

- New core module `stash`: SQLite-backed store at
  ~/.coven-code/stash.sqlite with kind, title, note, tags, project, and
  session metadata. Attachments are copied into
  ~/.coven-code/attachments/<id>/<filename> (full-uuid directory) so the
  stored copy survives the original moving or being deleted; lookups and
  removals are kind-scoped so /link cannot resolve or delete an
  attachment id (and vice versa).
- New slash commands /link (add/list/search/remove) and /attach
  (add/list/search/show/remove) with quote-aware argument parsing so
  paths and titles containing spaces work; unquoted attach paths with
  spaces also resolve.
- Both commands refuse to run in hosted review mode: the stash is a
  personal local store and must not mix into hosted tenant contexts.
- Registered in the command registry and TUI autocomplete; documented
  in docs/commands.md.

Verified with cargo fmt, check, clippy -D warnings, workspace tests,
and a live TUI smoke test (quoted-path attach, kind-scoped removal,
stored-copy cleanup).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mmands

/attach now sorts first in the palette, pushing /config below the
visible window on the initial capture. Anchor the open check on /clear
and assert /attach, /clear, and /compact instead; the /config filter
step still covers /config.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review follow-ups on the /link //attach stash:

- add_attachment removes the stored copy if the DB insert fails, so a
  locked/full database cannot leave orphaned files behind.
- remove() deletes the stored copy before the DB row; a filesystem
  failure keeps the row for retry, while an already-missing copy does
  not block removal.
- Fix the hosted-review docs link to use the ./configuration.md form.
- Add an end-to-end /link //attach round-trip test against a temp home
  covering quoted paths with spaces, kind-scoped removal, and stored
  copy cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
scan_buffer_for_urls consults COVEN_CODE_NO_HYPERLINKS, and
enabled_respects_env_var mutates that variable while sibling osc8
tests run in parallel, so scan assertions randomly saw an empty hit
list. Point the scan tests at the env-independent scan_buffer; the
env gate keeps its own dedicated test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@BunsDev
BunsDev merged commit 010c42c into main Jul 7, 2026
5 checks passed
@BunsDev
BunsDev deleted the stash-commands branch July 9, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants